﻿using Core;
using Core.CommandProcesser;
using Core.Entitys;
using Core.Enums;
using Core.Exceptions;
using Core.Tools;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace ConfigureSystem
{
    /// <summary>
    /// CommandFrame.xaml 的交互逻辑
    /// </summary>
    [Serializable]
    public partial class CommandFrame : Window
    {
        private static readonly IEnumerable ParamTypeItems;
        static CommandFrame()
        {
            var ParamTypes = (ParamTypes[])Enum.GetValues(typeof(ParamTypes));

            var ParamTypeItems = new KeyValuePair<string, ParamTypes>[ParamTypes.Length];

            for (int i = 0; i < ParamTypes.Length; i++)
            {
                var Item = ParamTypes[i];

                ParamTypeItems[i] = new KeyValuePair<string, Core.Entitys.ParamTypes>(Enum.GetName(typeof(ParamTypes), Item), Item);
            }

            CommandFrame.ParamTypeItems = ParamTypeItems;
        }

        private readonly CommandInfo CommandItem;

        public event Action<object, CommandInfo> Saved;

        public CommandFrame(CommandInfo CommandItem)
        {
            InitializeComponent();

            this.CommandItem = CommandItem;

            Name.Text = CommandItem.Name;
            Description.Text = CommandItem.Description;

            if ((CommandItem.Sign & CommandSigns.Select) != 0)
            {
                FunctionSign_Select.IsChecked = true;
            }
            else if ((CommandItem.Sign & CommandSigns.Insert) != 0)
            {
                FunctionSign_Insert.IsChecked = true;
            }
            else if ((CommandItem.Sign & CommandSigns.Update) != 0)
            {
                FunctionSign_Update.IsChecked = true;
            }
            else if ((CommandItem.Sign & CommandSigns.Delete) != 0)
            {
                FunctionSign_Delete.IsChecked = true;
            }

            if ((CommandItem.Sign & CommandSigns.Authorize) != 0)
            {
                ModifierSign_Authorize.IsChecked = true;
            }
            if ((CommandItem.Sign & CommandSigns.NoCall) != 0)
            {
                ModifierSign_NoCall.IsChecked = true;
            }

            switch (CommandItem.Type)
            {
                case "CSharp":
                    CSharpBox.IsSelected = true;
                    CSharpCode.Text = CommandItem.Code;
                    break;
                case "T-SQL":
                    SqlBox.IsSelected = true;
                    SqlCode.Text = CommandItem.Code;
                    if ((CommandItem.Sign & CommandSigns.SingleObjectResult) != 0)
                    {
                        ModifierSign_SingleObjectResult.IsChecked = true;
                    }
                    break;
                case "System":
                    SystemBox.IsSelected = true;
                    break;
            }
            if (CommandItem.Params != null)
            {
                foreach (var Item in CommandItem.Params)
                {
                    AddParamItem(Item);
                }
            }

            if (CommandItem.Results != null)
            {
                foreach (var Item in CommandItem.Results)
                {
                    AddResultItem(Item);
                }
            }
        }

        private void AddParamItem(CommandParamInfo ParamItem)
        {
            var NewParamPanel = App.Clone(TempParamPanel);

            NewParamPanel.Visibility = Visibility.Visible;

            var ParamDelete = (Button)NewParamPanel.FindName("ParamDelete");

            var TempLabel = (Label)TempParamPanel.FindName("ParamLabel");

            ParamDelete.Click += (Sender, Args) =>
            {
                var Index = ParamsPanel.Children.IndexOf(NewParamPanel);

                ParamsPanel.Children.RemoveAt(Index);

                for (int i = Index; i < ParamsPanel.Children.Count; ++i)
                {
                    var ItemLabel = (Label)((FrameworkElement)ParamsPanel.Children[i]).FindName("ParamLabel");

                    ItemLabel.Content = Helper.Format((string)TempLabel.Content, Name =>
                    {
                        switch (Name)
                        {
                            case "Index":
                                return i;
                            default:
                                return null;
                        }
                    });
                }
            };

            var ParamLabel = (Label)NewParamPanel.FindName("ParamLabel");

            ParamLabel.Content = Helper.Format((string)TempLabel.Content, Name =>
            {
                switch (Name)
                {
                    case "Index":
                        return ParamsPanel.Children.Count;
                    default:
                        return null;
                }
            });

            var ParamType = (ComboBox)NewParamPanel.FindName("ParamType");

            ParamType.SelectedValuePath = "Key";
            ParamType.DisplayMemberPath = "Key";
            ParamType.SelectedValuePath = "Value";

            ParamType.ItemsSource = ParamTypeItems;

            ParamType.SelectedValue = ParamItem.Type;

            if (ParamType.SelectedIndex == -1)
            {
                ParamType.SelectedIndex = 0;
            }

            if ((ParamItem.Sign & CommandParamSigns.AllowNull) != 0)
            {
                ((CheckBox)NewParamPanel.FindName("ParamAllowNull")).IsChecked = true;
            }

            ((TextBox)NewParamPanel.FindName("ParamName")).Text = ParamItem.Name;

            ((TextBox)NewParamPanel.FindName("ParamDescription")).Text = ParamItem.Description;

            ParamsPanel.Children.Add(NewParamPanel);
        }

        private void Command_Add_Param(object sender, RoutedEventArgs e)
        {
            AddParamItem(new CommandParamInfo());
        }

        private void AddResultItem(CommandParamInfo ResultItem)
        {
            var NewResultPanel = App.Clone(TempResultPanel);

            NewResultPanel.Visibility = Visibility.Visible;

            var ResultDelete = (Button)NewResultPanel.FindName("ResultDelete");

            var TempLabel = (Label)TempResultPanel.FindName("ResultLabel");

            ResultDelete.Click += (Sender, Args) =>
            {
                var Index = ResultsPanel.Children.IndexOf(NewResultPanel);

                ResultsPanel.Children.RemoveAt(Index);

                for (int i = Index; i < ResultsPanel.Children.Count; ++i)
                {
                    var ItemLabel = (Label)((FrameworkElement)ResultsPanel.Children[i]).FindName("ResultLabel");

                    ItemLabel.Content = Helper.Format((string)TempLabel.Content, Name =>
                    {
                        switch (Name)
                        {
                            case "Index":
                                return i;
                            default:
                                return null;
                        }
                    });
                }
            };

            var ResultLabel = (Label)NewResultPanel.FindName("ResultLabel");

            ResultLabel.Content = Helper.Format((string)TempLabel.Content, Name =>
            {
                switch (Name)
                {
                    case "Index":
                        return ResultsPanel.Children.Count;
                    default:
                        return null;
                }
            });

            var ResultType = (ComboBox)NewResultPanel.FindName("ResultType");

            ResultType.SelectedValuePath = "Key";
            ResultType.DisplayMemberPath = "Key";
            ResultType.SelectedValuePath = "Value";

            ResultType.ItemsSource = ParamTypeItems;

            ResultType.SelectedValue = ResultItem.Type;

            if (ResultType.SelectedIndex == -1)
            {
                ResultType.SelectedIndex = 0;
            }

            ((TextBox)NewResultPanel.FindName("ResultName")).Text = ResultItem.Name;

            ((TextBox)NewResultPanel.FindName("ResultDescription")).Text = ResultItem.Description;

            ResultsPanel.Children.Add(NewResultPanel);
        }

        private void Command_Add_Result(object sender, RoutedEventArgs e)
        {
            AddResultItem(new CommandParamInfo());
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            SqlCode.Width = 0;
            CSharpCode.Width = 0;
        }

        private void SqlCode_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            var Sender = sender as Control;

            if (Sender != null)
            {
                Sender.Width = ((Grid)Sender.Parent).ActualWidth;
            }
        }

        private void Cancel_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private bool SaveCommandInfo()
        {
            CommandItem.Name = Name.Text;

            if (string.IsNullOrEmpty(CommandItem.Name))
            {
                MessageBox.Show("Name can not be null.", "Operation tips");
                return false;
            }

            CommandItem.Description = Description.Text;

            if (string.IsNullOrEmpty(CommandItem.Description))
            {
                MessageBox.Show("Description can not be null.", "Operation tips");
                return false;
            }

            CommandItem.Sign = 0;

            if (FunctionSign_Select.IsChecked == true)
            {
                CommandItem.Sign = CommandSigns.Select;
            }
            else if (FunctionSign_Insert.IsChecked == true)
            {
                CommandItem.Sign = CommandSigns.Insert;
            }
            else if (FunctionSign_Update.IsChecked == true)
            {
                CommandItem.Sign = CommandSigns.Update;
            }
            else if (FunctionSign_Delete.IsChecked == true)
            {
                CommandItem.Sign = CommandSigns.Delete;
            }

            if (ModifierSign_Authorize.IsChecked == true)
            {
                CommandItem.Sign |= CommandSigns.Authorize;
            }
            if (ModifierSign_NoCall.IsChecked == true)
            {
                CommandItem.Sign |= CommandSigns.NoCall;
            }

            if (SqlBox.IsSelected)
            {
                CommandItem.Type = "T-SQL";

                CommandItem.Code = SqlCode.Text;

                if (ModifierSign_SingleObjectResult.IsChecked == true)
                {
                    CommandItem.Sign |= CommandSigns.SingleObjectResult;
                }

                if (string.IsNullOrEmpty(CommandItem.Code))
                {
                    MessageBox.Show("T-Sql code can not be null.", "Operation tips");
                    return false;
                }
            }
            else if (CSharpBox.IsSelected)
            {
                CommandItem.Type = "CSharp";

                CommandItem.Code = CSharpCode.Text;

                if (string.IsNullOrEmpty(CommandItem.Code))
                {
                    MessageBox.Show("C# code can not be null.", "Operation tips");
                    return false;
                }

                try
                {
                    CSharpProcesser.Build(CommandItem);
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                    return false;
                }
            }
            else if (SystemBox.IsSelected)
            {
                CommandItem.Type = "System";
            }
            else
            {
                MessageBox.Show("Invalid command type.", "Operation tips");
                return false;
            }

            CommandItem.Params = new CommandParamInfo[ParamsPanel.Children.Count - 1];

            int Index = 0;

            foreach (FrameworkElement Item in ParamsPanel.Children)
            {
                if (Item != TempParamPanel)
                {
                    var ParamItem = new CommandParamInfo();
                    ParamItem.Type = (ParamTypes)((ComboBox)Item.FindName("ParamType")).SelectedValue;

                    ParamItem.Sign = 0;

                    if (((CheckBox)Item.FindName("ParamAllowNull")).IsChecked == true)
                    {
                        ParamItem.Sign |= CommandParamSigns.AllowNull;
                    }

                    ParamItem.Name = ((TextBox)Item.FindName("ParamName")).Text;

                    if (string.IsNullOrEmpty(ParamItem.Name))
                    {
                        MessageBox.Show("Parameter name can not be null.", "Operation tips");
                        return false;
                    }

                    ParamItem.Description = ((TextBox)Item.FindName("ParamDescription")).Text;

                    if (string.IsNullOrEmpty(ParamItem.Description))
                    {
                        MessageBox.Show("Parameter description can not be null.", "Operation tips");
                        return false;
                    }

                    CommandItem.Params[Index] = ParamItem;

                    ++Index;
                }
            }

            CommandItem.Results = new CommandParamInfo[ResultsPanel.Children.Count - 1];

            Index = 0;

            foreach (FrameworkElement Item in ResultsPanel.Children)
            {
                if (Item != TempResultPanel)
                {
                    var ResultsItem = new CommandParamInfo();
                    ResultsItem.Type = (ParamTypes)((ComboBox)Item.FindName("ResultType")).SelectedValue;

                    ResultsItem.Sign = 0;

                    ResultsItem.Name = ((TextBox)Item.FindName("ResultName")).Text;

                    if (string.IsNullOrEmpty(ResultsItem.Name))
                    {
                        MessageBox.Show("Result name can not be null.", "Operation tips");
                        return false;
                    }

                    ResultsItem.Description = ((TextBox)Item.FindName("ResultDescription")).Text;

                    if (string.IsNullOrEmpty(ResultsItem.Description))
                    {
                        MessageBox.Show("Result description can not be null.", "Operation tips");
                        return false;
                    }

                    CommandItem.Results[Index] = ResultsItem;

                    ++Index;
                }
            }

            if (CommandItem.Id == 0)
            {
                ++CommandInfo.Identity;

                CommandItem.Id = CommandInfo.Identity;
            }

            Saved?.Invoke(this, CommandItem);

            return true;
        }

        private void Save_Click(object sender, RoutedEventArgs e)
        {
            if (SaveCommandInfo())
            {
                this.Close();
            }
        }

        private void CodeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ModifierSign_SingleObjectResult.Visibility = SqlBox.IsSelected ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}